export default class DragSelect {
  _multiSelectKeyPressed = false;
  _initialCursorPos = { x: 0, y: 0 };
  _newCursorPos = { x: 0, y: 0 };
  _previousCursorPos = { x: 0, y: 0 };
  _initialScroll = { x: 0, y: 0 };
  _selected = [];
  _prevSelected = [];
  _lastTouch;
  _breaked=false
  constructor({
    area = document,
    autoScrollSpeed = 1,
    callback = () => {},
    customStyles = false,
    hoverClass = "ds-hover",
    multiSelectKeys = ["ctrlKey", "shiftKey", "metaKey"],
    multiSelectMode = false,
    onDragMove = function () {},
    onDragStart = function () {},
    onDragStartBegin = function () {},
    onElementSelect = function () {},
    onElementUnselect = function () {},
    selectableClass = "ds-selectable",
    selectables = [],
    selectedClass = "ds-selected",
    selector = undefined, //创建元素的class
    selectorClass = "ds-selector",
    zoom = 1,
  }) {
    this.selectedClass = selectedClass;
    this.hoverClass = hoverClass;
    this.selectorClass = selectorClass;
    this.selectableClass = selectableClass;
    this.selectables = [];
    this._initialSelectables = this._toArray(selectables);
    this.multiSelectKeys = multiSelectKeys;
    this.multiSelectMode = multiSelectMode;
    this.autoScrollSpeed = autoScrollSpeed === 0 ? 0 : autoScrollSpeed;
    this.selectCallback = onElementSelect;
    this.unselectCallback = onElementUnselect;
    this.onDragStartBegin = onDragStartBegin;
    this.moveStartCallback = onDragStart;
    this.moveCallback = onDragMove;
    this.callback = callback;
    this.area = this._handleArea(area);
    this.customStyles = customStyles;
    this.zoom = zoom;
    // Selector
    this.selector = selector || this._createSelector();
    this.selector.classList.add(this.selectorClass); //添加ds-selector Class
    this.start();
  }
  break() {
    this._breaked = true;
    setTimeout(
      () => (this._breaked = false),
      100
    );
  }
  _handleArea(area) {
    if (area === document) return area;

    // 区域必须有一个特殊的位置属性用于计算
    const computedStyles = getComputedStyle(area);
    area.computedBorder = parseInt(computedStyles.borderWidth);
    const position = computedStyles.position;
    const isPositioned =
      position === "absolute" ||
      position === "relative" ||
      position === "fixed";

    //改变传入视口 的position
    if (!isPositioned) {
      area.style.position = "relative";
    }
    return area;
  }
  /**
   * 当选择对象没有提供时创建选择器节点
   */
  
  _createSelector() {
    var selector = document.createElement("div");

    selector.style.position = "absolute";
    if (!this.customStyles) {
      selector.style.background = "rgba(0, 0, 255, 0.1)";
      selector.style.border = "1px solid rgba(0, 0, 255, 0.45)";
      selector.style.display = "none";
      selector.style.pointerEvents = "none";
    }
    var _area = this.area === document ? document.body : this.area;
    _area.appendChild(selector);

    return selector;
  }

  start() {
    this._handleSelectables(this._initialSelectables);
    this.area.addEventListener("mousedown", this._startUp);
    this.area.addEventListener("touchstart", this._startUp, { passive: false });
  }
  setSelectables(nodes, removeFromSelection, addToSelection) {
    this.removeSelectables(this.getSelectables(), removeFromSelection);
    return this.addSelectables(nodes, addToSelection);
  }
  addSelectables(_nodes, addToSelection) {
    var nodes = this._toArray(_nodes);
    this._handleSelectables(nodes, false, addToSelection);
    return _nodes;
  }
  //选择可以选择的元素
  _handleSelectables(selectables, remove, fromSelection) {
    // console.log(selectables, remove, fromSelection);
    for (var index = 0; index < selectables.length; index++) {
      var selectable = selectables[index];
      var indexOf = this.selectables.indexOf(selectable);

      if (indexOf < 0 && !remove) {
        this._addSelectable(selectable, fromSelection);
      } else if (indexOf > -1 && remove) {
        this._removeSelectable(selectable, indexOf, fromSelection);
      }
    }
  }
  toggleSelection(_nodes, triggerCallback, special) {
    var nodes = this._toArray(_nodes);

    for (var index = 0, il = nodes.length; index < il; index++) {
      var node = nodes[index];

      if (this._selected.indexOf(node) < 0) {
        this.addSelection(node, triggerCallback, special);
      } else {
        // this.removeSelection(node, triggerCallback, special);
      }
    }

    return this._selected;
  }
  addSelection(_nodes, triggerCallback, dontAddToSelectables) {
    var nodes = this._toArray(_nodes);

    for (var index = 0, il = nodes.length; index < il; index++) {
      var node = nodes[index];
      this.select(node);
    }

    if (!dontAddToSelectables) {
      this.addSelectables(nodes);
    }
    if (triggerCallback) {
      this.callback(this._selected);
    }

    return this._selected;
  }
  _addSelectable(selectable, toSelection) {
    selectable.classList.add(this.selectableClass);  //给每个元素增加class
    // selectable.addEventListener('click', this._onClick);
    this.selectables.push(selectable);
    if (toSelection && this._selected.indexOf(selectable) < 0) {
      selectable.classList.add(this.selectedClass);
      this._selected.push(selectable);
    }
  }
  _removeSelectable(selectable, indexOf, fromSelection) {
    selectable.classList.remove(this.hoverClass);
    selectable.classList.remove(this.selectableClass);
    // selectable.removeEventListener('click', this._onClick);
    this.selectables.splice(indexOf, 1);
    if (fromSelection && this._selected.indexOf(selectable) > -1) {
      selectable.classList.remove(this.selectedClass);
      this._selected.splice(this._selected.indexOf(selectable), 1);
    }
  }
  select(item) {
    if (this._selected.indexOf(item) > -1) return false;  //当前选中的已经选中就跳出
    this._selected.push(item);
    item.classList.add(this.selectedClass);

    this.selectCallback(item);
    if (this._breaked) return false;

    return item;
  }
  unselect(item) {
    if (this._selected.indexOf(item) < 0) return false;

    this._selected.splice(this._selected.indexOf(item), 1);
    item.classList.remove(this.selectedClass);

    this.unselectCallback(item);
    // if (this._breaked) return false;

    return item;
  }
  _onClick = (event) => this.handleClick(event);
  handleClick(event) {
    if (this.mouseInteraction) {
      return;
    }
    if (this._isRightClick(event)) {
      return;
    }

    const node = event.target;

    if (this._isMultiSelectKeyPressed(event)) {
      this._prevSelected = this._selected.slice();
    } else {
      this._prevSelected = [];
    }

    this.checkIfInsideSelection(true);

    if (this.selectables.indexOf(node) > -1) {
      this.toggle(node);
    }

    // this._end(event);
  }
  _toArray(nodes) {
    if (!nodes) return []; //假如nodes 不传数组传了个0进来 return 空数组
    if (!nodes.length && this._isElement(nodes)) return [nodes];

    const array = [];
    for (let i = nodes.length - 1; i >= 0; i--) {
      array[i] = nodes[i];
    }
    return array;
  }
  _isElement(node) {
    try {
      return node instanceof HTMLElement || node instanceof SVGElement;
    } catch (e) {
      return (
        typeof node === "object" &&
        node.nodeType === 1 &&
        typeof node.style === "object" &&
        typeof node.ownerDocument === "object"
      );
    }
  }
  reset = (event) => {
    this._previousCursorPos = this._getCursorPos(event, this.area);
    document.removeEventListener("mouseup", this.reset);
    document.removeEventListener("touchend", this.reset);
    this.area.removeEventListener("mousemove", this._handleMove);
    this.area.removeEventListener("touchmove", this._handleMove);
    this.area.addEventListener("mousedown", this._startUp);
    this.area.addEventListener("touchstart", this._startUp, { passive: false });

    
    this.callback(this.getSelection(),event);

    // if (this._breaked) {
    //   return false;
    // }

    this.selector.style.width = "0";
    this.selector.style.height = "0";
    this.selector.style.display = "none";
    setTimeout(
      () =>
        // debounce in order "onClick" to work
        (this.mouseInteraction = false),
      100
    );
  };
  getSelection() {
    return [...this._selected];
  }
  stop(remove = true, fromSelection = true) {
    this.reset(false);
    this.area.removeEventListener('mousedown', this._startUp);
    this.area.removeEventListener('touchstart', this._startUp, {
      passive: false
    });
    // document.removeEventListener('mouseup', this._end);
    // document.removeEventListener('touchend', this._end);

    this._handleSelectables([...this.selectables], remove, fromSelection);
  }
  _startUp = (event) => this.startUp(event);
  startUp(event) {
    if (event.type == "touchstart") event.preventDefault();

    //回调函数
    this.onDragStartBegin(event);
    //开启
    this.selector.style.display = "block";
    this._getStartingPositions(event);
    this.checkIfInsideSelection(true);
    this.selector.style.display = "none";
    // this.moveStartCallback(event);
    this.area.removeEventListener("mousedown", this._startUp);
    this.area.removeEventListener("touchstart", this._startUp, {
      passive: false,
    });
    this.area.addEventListener("mousemove", this._handleMove);
    this.area.addEventListener("touchmove", this._handleMove);
    document.addEventListener("mouseup", this.reset);
    document.addEventListener("touchend", this.reset);
  }
  checkIfInsideSelection(force) {
    var anyInside = false;
    for (var i = 0, il = this.selectables.length; i < il; i++) {
      var selectable = this.selectables[i];

      // var scroll = this.getScroll(this.area);
      var selectionRect = {
        y: this.selector.getBoundingClientRect().top ,
        x: this.selector.getBoundingClientRect().left ,
        h: this.selector.offsetHeight,
        w: this.selector.offsetWidth,
      };
    
      if (this._isElementTouching(selectable, selectionRect)) {
        this._handleSelection(selectable, force);
        anyInside = true;
      } else {
        this._handleUnselection(selectable, force);
      }
    }
    return anyInside;    //选中元素的时候是true 不选中为false
  }
  _handleSelection(item, force) {
    if (item.classList.contains(this.hoverClass) && !force) {
      return false;
    }
    console.log(this._multiSelectKeyPressed);
    var posInSelectedArray = this._selected.indexOf(item);  //当前元素找不见的时候
    if (posInSelectedArray < 0) {   //元素找不见的时候 添加选中样式
      this.select(item);
    } else if (posInSelectedArray > -1 && false) {  //能找到这个元素 并且移动这个元素的位置时也保持选中状态
      this.unselect(item);
    }

    item.classList.add(this.hoverClass);
  }

  _isElementTouching(element, selectionRect, scroll) {
    const rect = element.getBoundingClientRect();  //当前点击元素的位置大小
    const elementRect = {
      y: rect.top / this.zoom ,
      x: rect.left / this.zoom,
      h: rect.height / this.zoom,
      w: rect.width / this.zoom,
    };
    //    b01
    // a01[1]a02
    //    b02      b11
    //          a11[2]a12
    //             b12
    if (
      selectionRect.x < elementRect.x + elementRect.w &&
      selectionRect.x + selectionRect.w > elementRect.x &&
      selectionRect.y < elementRect.y + elementRect.h &&
      selectionRect.h + selectionRect.y > elementRect.y
    ) {
      return true; // collision detected!
    } else {
      return false;
    }
  }
  _handleUnselection(item, force) {
    if (!item.classList.contains(this.hoverClass) && !force) {
      return false;
    }
    var posInSelectedArray = this._selected.indexOf(item);
    var isInPrevSelection = this._prevSelected.indexOf(item); // #9
    if (posInSelectedArray > -1 && isInPrevSelection < 0) {
      this.unselect(item);
    } else if (posInSelectedArray < 0 && isInPrevSelection > -1) {
      this.select(item);
    }

    item.classList.remove(this.hoverClass);
  }
  _getStartingPositions(event) {
    this._initialCursorPos = this._newCursorPos = this._getCursorPos(
      event,
      this.area
    );
    this._initialScroll = this.getScroll(this.area);
    var selectorPos = {};
    selectorPos.x = this._initialCursorPos.x + this._initialScroll.x;
    selectorPos.y = this._initialCursorPos.y + this._initialScroll.y;
    selectorPos.w = 0;
    selectorPos.h = 0;
    this._updatePos(this.selector, selectorPos);
  }
  _updatePos(node, pos) {
    node.style.left = pos.x + "px";
    node.style.top = pos.y + "px";
    node.style.width = pos.w + "px";
    node.style.height = pos.h + "px";
    return node;
  }
  _handleMove = event => this.handleMove(event);
  handleMove (event) {
    var selectorPos = this._getPosition(event);
    this.moveCallback(event);
    if (this._breaked) return false;
    this.selector.style.display = "block";

    this._updatePos(this.selector, selectorPos);
    this.checkIfInsideSelection(null);    //框选增加class    =====及其重要 =======

    // this._setScrollState(event);
  };
  _setScrollState(event) {
    const edge = this.isCursorNearEdge(this.area, event);

    if (edge) {
      if (this._autoScrollInterval)
        window.clearInterval(this._autoScrollInterval);

      this._autoScrollInterval = window.setInterval(() => {
        this._updatePos(this.selector, this._getPosition(event));
        this.checkIfInsideSelection(null);
        this._autoScroll(edge);
      });
    } else if (!edge && this._autoScrollInterval) {
      window.clearInterval(this._autoScrollInterval);
      this._autoScrollInterval = null;
    }
  }
  isCursorNearEdge(area, event) {
    if (typeof area === "object" && this._isElement(event)) {
      console.warn("[DragSelect] DEPRECATION warning: this method signature is changing. From isCursorNearEdge(event, area) to isCursorNearEdge(area, event). Please use area as first argument and event as second. It will still work for now but functionality be removed soon")
      const _event = event
      const _area = area
      area = _event
      event = _area
    }

    var cursorPosition = this._getCursorPos(area, event);
    var areaRect = this._getAreaRect(area);

    var tolerance = {
      x: Math.max(areaRect.width / 10, 30),
      y: Math.max(areaRect.height / 10, 30)
    };

    if (cursorPosition.y < tolerance.y) {
      return 'top';
    } else if (areaRect.height - cursorPosition.y < tolerance.y) {
      return 'bottom';
    } else if (areaRect.width - cursorPosition.x < tolerance.x) {
      return 'right';
    } else if (cursorPosition.x < tolerance.x) {
      return 'left';
    }

    return false;
  }
  _autoScroll(event) {
    var edge = this.isCursorNearEdge(event, this.area);
    var docEl = document && document.scrollTop;
    var _area = this.area === document ? docEl || document.body : this.area;
    if (edge === "top" && _area.scrollTop > 0) {
      _area.scrollTop -= 1 * this.autoScrollSpeed;
    } else if (edge === "bottom") {
      _area.scrollTop += 1 * this.autoScrollSpeed;
    } else if (edge === "left" && _area.scrollLeft > 0) {
      _area.scrollLeft -= 1 * this.autoScrollSpeed;
    } else if (edge === "right") {
      _area.scrollLeft += 1 * this.autoScrollSpeed;
    }
  }
  isCursorNearEdge(event, area) {
    //选中区域边界检测
    var cursorPosition = this._getCursorPos(event, area); //当前点击位置
    var areaRect = this.getAreaRect(area); //当前所在视口的宽高
    var tolerance = {
      //公差  视口固定的话此值固定
      x: Math.max(areaRect.width / 10, 30),
      y: Math.max(areaRect.height / 10, 30),
    };
    if (cursorPosition.y < tolerance.y) {
      return "top";
    } else if (areaRect.height - cursorPosition.y < tolerance.y) {
      return "bottom";
    } else if (areaRect.width - cursorPosition.x < tolerance.x) {
      return "right";
    } else if (cursorPosition.x < tolerance.x) {
      return "left";
    }
    return false;
  }
  _getPosition(event) {
    var cursorPosNew = this._getCursorPos(event, this.area); //当前鼠标的xy坐标
    var scrollNew = this.getScroll(this.area);
    this._newCursorPos = cursorPosNew;
    var scrollAmount = {
      x: scrollNew.x - this._initialScroll.x,
      y: scrollNew.y - this._initialScroll.y,
    };
    var selectorPos = {};
    // right
    if (cursorPosNew.x > this._initialCursorPos.x - scrollAmount.x) {
      selectorPos.x = this._initialCursorPos.x + this._initialScroll.x; // 2.
      selectorPos.w =
        cursorPosNew.x - this._initialCursorPos.x + scrollAmount.x; // 3.
    } else {
      selectorPos.x = cursorPosNew.x + scrollNew.x;
      selectorPos.w =
        this._initialCursorPos.x - cursorPosNew.x - scrollAmount.x;
    }

    if (cursorPosNew.y > this._initialCursorPos.y - scrollAmount.y) {
      selectorPos.y = this._initialCursorPos.y + this._initialScroll.y;
      selectorPos.h =
        cursorPosNew.y - this._initialCursorPos.y + scrollAmount.y;
    } else {
      selectorPos.y = cursorPosNew.y + scrollNew.y;
      selectorPos.h =
        this._initialCursorPos.y - cursorPosNew.y - scrollAmount.y;
    }
    return selectorPos;
  }
  _getCursorPos(event, area) {
    if (!event) return { x: 0, y: 0 };
    if (event.touches && event.type !== "touchend") {
      this._lastTouch = event;
    }
    event = event.touches ? this._lastTouch.touches[0] : event;
    var cPos = {
      x: event.pageX || event.clientX,
      y: event.pageY || event.clientY,
    };

    var areaRect = this.getAreaRect(area || document); //当前画线区域所在父盒子位置坐标信息
    var docScroll = this.getScroll();
    return {
      x: cPos.x - areaRect.left - docScroll.x,
      y: cPos.y - areaRect.top - docScroll.y,
    };
  }
  getAreaRect(area) {
    if (area === document) {
      var size = {
        y:
          area.documentElement.clientHeight > 0
            ? area.documentElement.clientHeight
            : window.innerHeight,
        x:
          area.documentElement.clientWidth > 0
            ? area.documentElement.clientWidth
            : window.innerWidth
      };
      return {
        top: 0,
        left: 0,
        bottom: 0,
        right: 0,
        width: size.x,
        height: size.y
      };
    }

    const rect = area.getBoundingClientRect();
    return {
      top: rect.top,
      left: rect.left,
      bottom: rect.bottom,
      right: rect.right,
      width: area.clientWidth || rect.width,
      height: area.clientHeight || rect.height
    };
  }
  getScroll(area) {
    var body = {
      top:
        document.body.scrollTop > 0
          ? document.body.scrollTop
          : document.documentElement.scrollTop,
      left:
        document.body.scrollLeft > 0
          ? document.body.scrollLeft
          : document.documentElement.scrollLeft,
    };

    var scroll = {
      y: area && area.scrollTop >= 0 ? area.scrollTop : body.top,
      x: area && area.scrollLeft >= 0 ? area.scrollLeft : body.left,
    };

    return scroll;
  }
}
